GLOBAL.setmetatable(env,{__index=function(t,k) return GLOBAL.rawget(GLOBAL,k) end})
local MINI_OFFSET = 0.4

local function GetKeyFromConfig(config)
    local key = GetModConfigData(config, true)
    if key == "no_toggle_key" then
        key = -1
    elseif type(key) == "string" then
        key = rawget(_G, key)
    end
    return type(key) == "number" and key or -1
end

local config_key_handlers = {
    keydown = {},
    keyup = {}
}

local function GetKeyHandlersForConfig(event, config)
    if not config_key_handlers[event][config] then
        config_key_handlers[event][config] = {}
    end
    return config_key_handlers[event][config]
end

local function InGame()
    return ThePlayer and ThePlayer.HUD and not ThePlayer.HUD:HasInputFocus()
end

local function AddKeyHandlerForConfig(down, config, func, options)
    options = options or {}
    local key = GetKeyFromConfig(config)
    if key ~= -1 then
        local event = down and "keydown" or "keyup"
        local handlers = GetKeyHandlersForConfig(event, config)
        if not options.can_duplicate then
            for handler in pairs(handlers) do
                handler:Remove()
            end
        end
        local fn = function()
            if options.ingame_only == false or InGame() then
                func(options.data)
            end
        end
        local handler = down and TheInput:AddKeyDownHandler(key, fn) or TheInput:AddKeyUpHandler(key, fn)
        local on_remove = handler.Remove
        handler.Remove = function(self, ...)
            handlers[handler] = nil
            return on_remove(self, ...)
        end
        handlers[handler] = true
        return handler

    end
end

local function AddKeyUpHandlerForConfig(config, func, options)
    return AddKeyHandlerForConfig(false, config, func, options)
end

local function AddKeyDownHandlerForConfig(config, func, options)
    return AddKeyHandlerForConfig(true, config, func, options)
end

local function SendAction(act, fn)
    local playercontroller = ThePlayer.components.playercontroller
    if playercontroller.ismastersim then
        ThePlayer.components.combat:SetTarget(nil)
        playercontroller:DoAction(act)
        return
    end

    if playercontroller.locomotor then
        act.preview_cb = fn
        playercontroller:DoAction(act)
    else
        fn()
    end
end
-----------------------------------以上来源于tony哥的轮子,没有导入直接用了------------------------------------------

local function table_leng(t)
    local leng = 0
    for k, v in pairs(t) do
        leng = leng + 1
    end
    return leng
end

local function OffsetPos(point1, point2, distance) -- 将point1向point2偏移distance,返回point1的位置
	point1.x = point1.x - ((point1.x - point2.x)/math.abs(point1.x - point2.x))*math.cos(math.atan((point1.z - point2.z)/(point1.x - point2.x)))*distance -- 偏移后的x
	point1.z = point1.z - ((point1.z - point2.z)/math.abs(point1.z - point2.z))*math.abs(math.sin(math.atan((point1.z - point2.z)/(point1.x - point2.x))))*distance	--偏移后的z
    return point1
end

local function FindItemFromTile(prefab, num) -- 从地皮上寻找给定数目的实体, 如果num不提供,则返回找到的第一个
	local prefabs = {}
	local pos = ThePlayer:GetPosition()
	for k,v in pairs(TheSim:FindEntities(pos.x,0,pos.z,50,nil,{"INLIMBO"})) do
		if v and v:IsValid() and v.prefab == prefab then
			prefabs[#prefabs + 1] = v
		end
	end
    if not num then
        return prefabs[1]
    elseif num == #prefabs then
        return prefabs
    end
    return nil
end

local function WalkToTarget(target) -- 走向目标偏移后的位置,到达后返回true
    local target_pos = target.pos or target:GetPosition()
    SendAction(BufferedAction(ThePlayer, nil, ACTIONS.WALKTO, nil, target_pos), function ()
        SendRPCToServer(RPC.LeftClick, ACTIONS.WALKTO.code, target_pos.x, target_pos.z)
    end)
    while true do
        Sleep(GLOBAL.FRAMES * 5)
        local nowPlayer_pos = ThePlayer:GetPosition()
        local distSq = nowPlayer_pos:DistSq(target_pos)
        if distSq > MINI_OFFSET then -- 玩家到达指定地点返回true
            SendAction(BufferedAction(ThePlayer, nil, ACTIONS.WALKTO, nil, target_pos), function ()
                SendRPCToServer(RPC.LeftClick, ACTIONS.WALKTO.code, target_pos.x, target_pos.z)
            end)
        else
            return true
        end
    end
end 

local center,smalls
local rightPoints = {}
local function Archive_Init()   -- 对基座和点位进行初始化
    center = FindItemFromTile("archive_orchestrina_base")
    if not center then
        ThePlayer.components.talker:Say("档案馆不在周围")
        return false
    end
    center.pos = center:GetPosition()
    smalls = FindItemFromTile("archive_orchestrina_small", 8)
    if smalls and table_leng(smalls) == 8 then
        for _, small in pairs(smalls) do
            small.pos = OffsetPos(small:GetPosition(), center.pos, 1)
            small.flag = nil
        end
        return true
    else
        ThePlayer.components.talker:Say("档案馆不在周围")
        return false
    end
end

local function ResetWrongPoints()   -- 重置错误点的状态
    for _, small in pairs(smalls) do
        if small.flag ~= 1 then
            small.flag = nil
        end
    end
end

local function ResetAll()
    if smalls then
        for _, small in pairs(smalls) do
            small.pos = nil
            small.flag = nil
            small = nil
        end
    end
    if rightPoints then
        for _, point in pairs(rightPoints) do
            point.pos = nil
            point.flag = nil
            point = nil
        end
    end
    center = nil
    rightPoints = {}
    smalls = {}
end

local function ToggleLagCompensation()  -- 用来切补偿的
    local playercontroller = ThePlayer.components.playercontroller
    local movementprediction = not Profile:GetMovementPredictionEnabled()
    if playercontroller:CanLocomote() then
        playercontroller.locomotor:Stop()
    else
        playercontroller:RemoteStopWalking()
    end
    ThePlayer:EnableMovementPrediction(movementprediction)
    Profile:SetMovementPredictionEnabled(movementprediction)
end

local ArchiveTask_thread = nil
local locomotor_lag = false
local function ClearArchiveTaskThread() -- 杀线程
    if ArchiveTask_thread then
        print("Thread cleared:", ArchiveTask_thread.id)
        KillThreadsWithID(ArchiveTask_thread.id)
        ResetAll()
        ArchiveTask_thread:SetList(nil)
        ArchiveTask_thread = nil
        if locomotor_lag then   -- 切回去原来的补偿状态
            ToggleLagCompensation()
        end
    end
end

local function GoToNextPoint() -- 这个函数能找出与当前所在点距离最近的一个不明状态点
    local MinDistance = 10000
    local point
    for _, small in pairs(smalls) do
        if not small.flag then
            local Distance = ThePlayer:GetPosition():DistSq(small.pos)
            if Distance < MinDistance then
                MinDistance = Distance
                point = small
            end
        end
    end
    if point then -- Todo 如果因为各种原因没有走过去，需要进行相应的处理
        local arrive
        local num = 0
        repeat 
            arrive = WalkToTarget(point)
            num = num +1
            if num == 8 then
                print("你好卡啊")
                break
            end
        until arrive -- 暂时是重复走，如果需要可以在这里改
    end
    return point
end

local function GoToRightPoints()            -- 走一遍对的点
    local point
    local arrive
    if table_leng(rightPoints) == 0 then    -- 如果对的表为空
        point = GoToNextPoint()             -- 直接走向下一个点
        return point
    end
    for _, _point in pairs(rightPoints) do  -- 走一遍已经对的点
        arrive = WalkToTarget(_point)
        if arrive then
            point = _point
        else
            return false
        end
    end
    return point
end

local anim_all = {"one", "two", "three", "four", "five", "six", "seven", "eight"}
local function isanim(entity,anim)  -- 来自呼吸的判断动画
    return entity and entity.AnimState and entity.AnimState:IsCurrentAnimation(anim)
end

local function CheckNowPoint(ent)
	for i,v in ipairs(anim_all) do
		if isanim(ent, v) or isanim(ent, v.."_pre") then
			return i 
		end
	end
end

local function GetItem()    -- 来自呼吸的找东西
	local items = {"archive_lockbox", "blank_certificate",}
    if not ThePlayer and ThePlayer.replica.inventory then return false end
	for i,item in ipairs(items) do
		for container,v in pairs(ThePlayer.replica.inventory:GetOpenContainers()) do
			if container and container.replica and container.replica.container
			then
				local items_container = container.replica.container:GetItems()
				for k,v in pairs(items_container) do
					if v.prefab == item then
						return v, k, container.replica.container
					end
				end
			end
		end
		for k,v in pairs(ThePlayer.replica.inventory:GetItems()) do
			if v.prefab == item then
				return v, k, ThePlayer.replica.inventory
			end
		end
	end
    return false
end

local function GoToCenterAndPutDown()
    local item = GetItem()
    if item then
        WalkToTarget(center)
        ThePlayer.replica.inventory:DropItemFromInvTile(item)
        ThePlayer.components.talker:Say("放下蒸馏知识")
        return true
    end
    ThePlayer.components.talker:Say("没有携带蒸馏知识,打断后请捡起蒸馏知识后重新进行")
    return false
end

local function ArchiveTask()
    if not InGame() then return end			-- 不在游戏中退出
	if not Archive_Init()  then return end	-- 初始化失败退出
    ArchiveTask_thread = StartThread(function ()
        if ThePlayer.components.playercontroller.locomotor then -- 麻瓜办法,既然关延迟不行,就检测如果关了帮开
            locomotor_lag = true
            ToggleLagCompensation() 
        end
        if GoToCenterAndPutDown() then
            Sleep(FRAMES * 10)
            local num = 0
            local point = GoToNextPoint()
            while true and point do
                if CheckNowPoint(point) then
                    if point.flag ~= 1 then
                        num = num + 1               -- 新的点索引加1
                        rightPoints[num] = point    -- 记录新的点到表中
                        point.flag = 1              -- 表示对的点
                        ResetWrongPoints()          -- 除对的点外其他点的flag重置为nil 
                    end
                    point = GoToNextPoint()         -- 去下一个点
                else
                    point.flag = 0                  -- 表示错的点
                    point = GoToRightPoints()       -- 走一遍对的点
                end
                if table_leng(rightPoints) == table_leng(smalls) - 1 then
                    ThePlayer.components.talker:Say("任务完成")
                    break
                end
            end
        end
        ClearArchiveTaskThread() -- 任务做完全清
    end, "ArchiveTask_thread_id")
end

AddKeyUpHandlerForConfig("bug_key", ArchiveTask)

local interrupt_controls = {}
local mouse_controls = {[_G.CONTROL_PRIMARY] = false, [_G.CONTROL_SECONDARY] = true}
for control = _G.CONTROL_ATTACK, _G.CONTROL_MOVE_RIGHT do
    interrupt_controls[control] = true
end

AddComponentPostInit("playercontroller", function(self, inst)
    if inst ~= ThePlayer then return end
    local PlayerControllerOnControl = self.OnControl
    self.OnControl = function(self, control, down)
        local mouse_control = mouse_controls[control]
        PlayerControllerOnControl(self, control, down)
        if down and ArchiveTask_thread and InGame() and (interrupt_controls[control] or mouse_control ~= nil and not TheInput:GetHUDEntityUnderMouse()) then
            ThePlayer.components.talker:Say("任务中断")
            ClearArchiveTaskThread()
        end
    end
end)